﻿using System;
using System.Collections.Generic;
using System.Linq;
using TiaoTiaoCode.Common.Extensions;

namespace TiaoTiaoCode.Common.Helpers
{
    public static class TiaoTiaoSpc
    {
        /// <summary>
        /// 获取SPC移动极差-MR
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static List<double> SpcMoveRs(this IEnumerable<double> values)
        {
            var lst = new List<double>();

            var vas = values.ToList();
            var len = values.Count();
            for (var i = 0; i < len - 1; i++)
            {
                lst.Add(Math.Abs(vas[i + 1] - vas[i]));
            }

            return lst;
        }

        /// <summary>
        /// 获取SPC极差-R
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static double SpcRs(this IEnumerable<double> values)
        {
            return values.Max() - values.Min();
        }

        #region SPC 八原则判断

        /// <summary>
        /// SPC八原则：一点落在控制限之外
        /// </summary>
        /// <param name="values"></param>
        /// <param name="high">上限</param>
        /// <param name="low">下限</param>
        /// <returns></returns>
        public static bool SpcIsAlarmPrinciple1(this IEnumerable<double> values, double high, double low)
        {
            return values.Any(t => t > high || t < low);
        }

        /// <summary>
        /// SPC八原则：续九点在中心线同一侧
        /// </summary>
        /// <param name="values"></param>
        /// <param name="center"></param>
        /// <returns></returns>
        public static bool SpcIsAlarmPrinciple2(this IEnumerable<double> values, double center)
        {
            if (values.Count() < 9) return false;

            var list = values.ToList();
            var len = values.Count();

            for (var i = 0; i < len; i++)
            {
                // 取连续9个点
                var tmpList = list.Skip(i).Take(9);

                // 不足9个点，则为不报警
                if (tmpList.Count() < 9) return false;

                // 中心线正向一侧
                var b = tmpList.All(t => t > center);
                if (b) return true;

                // 中心线负向一侧
                b = tmpList.All(t => t < center);
                if (b) return true;
            }

            return false;
        }

        /// <summary>
        /// SPC八原则：连续六点递增或递减
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static bool SpcIsAlarmPrinciple3(this IEnumerable<double> values)
        {
            if (values.Count() < 6) return false;

            var list = values.ToList();
            var len = values.Count();

            for (var i = 0; i < len; i++)
            {
                // 取连续6个点
                var tmpList = list.Skip(i).Take(6);

                // 不足6个点，则为不报警
                if (tmpList.Count() < 6) return false;

                // 连续6点递增
                var b = tmpList.TiaoTiaoIsIncrease();
                if (b) return true;

                // 连续6点递减
                b = tmpList.TiaoTiaoIsDecrease();
                if (b) return true;
            }

            return false;
        }

        /// <summary>
        /// SPC八原则：连续十四个点交替上下
        /// </summary>
        /// <param name="values"></param>
        /// <returns></returns>
        public static bool SpcIsAlarmPrinciple4(this IEnumerable<double> values)
        {
            if (values.Count() < 14) return false;

            var count = 0;

            var list = values.ToList();

            // 第一个点
            var first = list.First();
            list.RemoveAt(0);

            // 第二个点
            var last = list.First();
            list.RemoveAt(0);

            // 判断前两个数据是上升还是下降趋势
            var isUp = first < last;// 上升

            // 判断其余
            foreach (var v in list)
            {
                // 上一对点为上升，则这次判断下降
                if (isUp)
                {
                    var b = v < last;
                    // 下降
                    if (b)
                    {
                        count++;
                        if (count >= 14) return true;
                        isUp = !b;
                    }
                    // 还是上升
                    else
                    {
                        count = 0;
                        isUp = true;
                    }
                    last = v;
                }
                // 上一对点为下降，则这次判断上升
                else
                {
                    var b = v > last;
                    // 上升
                    if (b)
                    {
                        count++;
                        if (count >= 14) return true;
                        isUp = b;
                    }
                    // 还是下降
                    else
                    {
                        count = 0;
                        isUp = false;
                    }
                    last = v;
                }
            }

            // 返回是否连续14个点交替上下
            return count >= 14;
        }

        /// <summary>
        /// SPC八原则：连续三点中，有两点落在中心线同一侧的两倍sigma区以外
        /// </summary>
        /// <param name="values"></param>
        /// <param name="center"></param>
        /// <param name="sigma"></param>
        /// <returns></returns>
        public static bool SpcIsAlarmPrinciple5(this IEnumerable<double> values, double center, double sigma)
        {
            if (values.Count() < 3) return false;

            var list = values.ToList();
            var len = values.Count();

            var nSigma = 2 * sigma;

            // 正向2倍区
            var upLimit = center + nSigma;

            // 负向2倍区
            var lowLimit = center - nSigma;

            // 判断
            for (var i = 0; i < len; i++)
            {
                // 取出连续3个点
                var tmpList = list.Skip(i).Take(3);

                // 不足3个点，则为不报警
                if (tmpList.Count() < 3) return false;

                // 正向一倍区
                var count = tmpList.Count(t => t > center && t < upLimit);
                if (count >= 2) return true;

                // 负向一倍区
                count = tmpList.Count(t => t < center && t > lowLimit);
                if (count >= 2) return true;
            }

            return false;
        }

        /// <summary>
        /// SPC八原则：连续五点中，有四点落在中心线同一侧的一倍sigma区以外
        /// </summary>
        /// <param name="values"></param>
        /// <param name="center"></param>
        /// <param name="sigma"></param>
        /// <returns></returns>
        public static bool SpcIsAlarmPrinciple6(this IEnumerable<double> values, double center, double sigma)
        {
            if (values.Count() < 5) return false;

            var list = values.ToList();
            var len = values.Count();

            // 正向1倍区
            var upLimit = center + sigma;

            // 负向1倍区
            var lowLimit = center - sigma;

            // 判断
            for (var i = 0; i < len; i++)
            {
                // 取5个点
                var tmpList = list.Skip(i).Take(5);

                // 不足5个点，则为不报警
                if (tmpList.Count() < 5) return false;

                // 正向一倍区
                var count = tmpList.Count(t => t > center && t < upLimit);
                if (count >= 4) return true;

                // 负向一倍区
                count = tmpList.Count(t => t < center && t > lowLimit);
                if (count >= 4) return true;
            }

            return false;
        }

        /// <summary>
        /// SPC八原则：连接十五点落在中心线两侧的一倍sigma区内
        /// </summary>
        /// <param name="values"></param>
        /// <param name="center"></param>
        /// <param name="sigma"></param>
        /// <returns></returns>
        public static bool SpcIsAlarmPrinciple7(this IEnumerable<double> values, double center, double sigma)
        {
            if (values.Count() < 15) return false;

            var list = values.ToList();
            var len = values.Count();

            // 正向1倍区
            var upLimit = center + sigma;

            // 负向1倍区
            var lowLimit = center - sigma;

            for (var i = 0; i < len; i++)
            {
                // 取出连续15个点
                var tmpList = list.Skip(i).Take(15);

                // 不足15个点，则为不报警
                if (tmpList.Count() < 15) return false;

                // 判断
                var b = tmpList.All(t => (t > center && t < upLimit) || (t < center && t > lowLimit));
                if (b) return true;
            }

            return false;
        }

        /// <summary>
        /// SPC八原则：连续八点落在中心线两侧，且无一在一倍sigma区内
        /// </summary>
        /// <param name="values"></param>
        /// <param name="center"></param>
        /// <param name="sigma"></param>
        /// <returns></returns>
        public static bool SpcIsAlarmPrinciple8(this IEnumerable<double> values, double center, double sigma)
        {
            if (values.Count() < 8) return false;

            var list = values.ToList();
            var len = values.Count();

            for (var i = 0; i < len; i++)
            {
                // 取连续的8个点
                var tmpList = list.Skip(i).Take(8);

                // 不足8个，则为不报警
                if (tmpList.Count() < 8) return false;

                // 判断是否在中心线一侧
                var sameEdge = tmpList.All(t => t > center);
                // 都在中心线正向区
                if (sameEdge) continue;
                // 都在中心线负向区
                sameEdge = tmpList.All(t => t < center);
                if (sameEdge) continue;

                // 连续8个点，在中心线两侧
                // 判断是否有在一倍区的
                var b = tmpList.Any(t => (t > center && t < (center + sigma)) || (t < center && t > (center - sigma)));
                // 有在一倍区的，则不满足，判断下一组
                if (b) continue;

                // 无在一倍区的，则满足，返回报警
                return true;
            }

            return false;
        }

        #endregion
    }
}
